/*
 * BLink.java
 *
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.core.basenet;

import org.fpdev.util.FPUtil;
import com.vividsolutions.jts.geom.Coordinate;
import java.io.IOException;
import org.fpdev.core.transit.*;
import org.fpdev.core.transit.SubRouteInfo;
import org.fpdev.apps.rtemaster.GUIOutputAcceptor;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.fpdev.core.FPEngine;
import org.fpdev.core.Scenario;
import org.fpdev.apps.rtemaster.gui.map.MapCanvas;

/**
 * The abstract base class for a BaseNetwork link.
 * 
 * @author demory
 */
public abstract class BLink implements Serializable {

  // An enumeration for indicating the specific Java class used to implement
  // a given link, not to be confused with the user-defined link type. 
  // This property should generally stay hidden from the user in all cases.
  //
  // TODO: replace w/ enum
  public final static int CLASS_STREET = 1;
  public final static int CLASS_STREETPARTIAL = 2;
  public final static int CLASS_BIKEPED = 3;
  public final static int CLASS_METRO = 4;
  public final static int CLASS_LRT = 5;
  public final static int CLASS_RAILROAD = 7;
  public final static int CLASS_DIRECT = 10;

  protected int id_;

  protected transient Scenario scenario_;
  protected short scenID_;

  protected transient BNode fNode_,  tNode_;
  protected int fNodeID_,  tNodeID_;

  protected transient double x1_,  y1_,  x2_,  y2_;
  protected transient double bbx1_,  bby1_,  bbx2_,  bby2_;

  protected boolean typeOverride_;

  protected List<Point2D.Double> shpPts_;

  protected String displayName_;

  protected double lenFt_;

  // registered paths
  protected transient Set<Path> paths_;

  // transit fields
  protected transient List<SubRouteInfo> srisFT_,  srisTF_;
  protected transient Station station_;
  protected transient short onRteNet_;

  // elevation fields
  protected int[] elevArr_;
  protected float upSlopeFW_,  upSlopeBW_;

  public BLink(Scenario s, int id, BNode fNode, BNode tNode, String dispName, int lenFt) {
    scenario_ = s;
    scenID_ = s.getID();
    id_ = id;
    fNode_ = fNode;
    tNode_ = tNode;
    fNodeID_ = fNode_.getID();
    tNodeID_ = tNode_.getID();
    onRteNet_ = 0;

    updateXY();
    upSlopeFW_ = upSlopeBW_ = 0;

    displayName_ = dispName.trim();

    lenFt_ = lenFt;
    station_ = null;
    paths_ = new HashSet<Path>(); //new NodeSequence();
    srisFT_ = new LinkedList<SubRouteInfo>();
    srisTF_ = new LinkedList<SubRouteInfo>();
    shpPts_ = new ArrayList<Point2D.Double>(); //NodeSequence();
    typeOverride_ = false;
    updateBBXY();

  }	// end of constructor

  public BLink(BLink link) {
    this(link.scenario_, link.id_, link.fNode_, link.tNode_, link.displayName_, link.getLengthFeet());
    shpPts_ = link.shpPts_;
  }

  //

  public void init(FPEngine engine) {
    scenario_ = engine.getScenarios().get(scenID_);
    //if(scenario_ == null) System.out.println("null scenario! id="+scenID_);
    fNode_ = engine.getBaseNet().getNodeFromID(fNodeID_);
    tNode_ = engine.getBaseNet().getNodeFromID(tNodeID_);

    paths_ = new HashSet<Path>(); //new NodeSequence();
    srisFT_ = new LinkedList<SubRouteInfo>();
    srisTF_ = new LinkedList<SubRouteInfo>();

    updateXY();
    updateBBXY();
  }

  //////////////////// ACCESSOR/MUTATOR METHODS ////////////////////

  public Scenario getScenario() {
    return scenario_;
  }

  public void setScenario(Scenario s) {
    scenario_= s;
  }

  public boolean getTypeOverride() {
    return typeOverride_;
  }

  public void setTypeOverride(boolean val) {
    typeOverride_ = val;
  }

  public int getID() {
    return id_;
  }

  public int getFNodeID() {
    return fNode_.getID();
  }

  public int getTNodeID() {
    return tNode_.getID();
  }

  public BNode getFNode() {
    return fNode_;
  }

  public BNode getTNode() {
    return tNode_;
  }

  public void setFNode(BNode fNode) {
    fNode_ = fNode;
    fNodeID_ = fNode.getID();
    x1_ = fNode.getX();
    y1_ = fNode.getY();
  }

  public void setTNode(BNode tNode) {
    tNode_ = tNode;
    tNodeID_ = tNode.getID();
    x2_ = tNode.getX();
    y2_ = tNode.getY();
  }

  public void swapEndPoints() {
    BNode temp = fNode_;
    fNode_ = tNode_;
    tNode_ = temp;
    //Collections.reverse(shpPts_);
    updateXY();
  //System.out.println("reversed link "+id_+", shpPts size="+shpPts_.size());
  }

  public String getTileStr() {
    return ""; // temp
  }

  public short getOnRteNet() {
    return onRteNet_;
  }

  public void setOnRteNet(short val) {
    onRteNet_ = val;
  }

  public abstract int getType();

  public int getExtendedType() {
    return getType() * 100;
  }

  public void setType(int type) {
  } // overridden by applicable subclasses; better approach needed longterm?

  public abstract int getClassType();

  public void updateXY() {
    x1_ = fNode_.getX();
    y1_ = fNode_.getY();
    x2_ = tNode_.getX();
    y2_ = tNode_.getY();
  }

  public void updateBBXY() {
    Rectangle2D bbox = getBoundingBox();
    bbx1_ = bbox.getMinX();
    bby1_ = bbox.getMinY();
    bbx2_ = bbox.getMaxX();
    bby2_ = bbox.getMaxY();
  }

  public double getX1() {
    return x1_;
  }

  public double getY1() {
    return y1_;
  }

  public double getX2() {
    return x2_;
  }

  public double getY2() {
    return y2_;
  }

  public double getBBX1() {
    return bbx1_;
  }

  public double getBBY1() {
    return bby1_;
  }

  public double getBBX2() {
    return bbx2_;
  }

  public double getBBY2() {
    return bby2_;
  }

  public String getDisplayName() {
    return displayName_;
  }

  public String getDisplayText() {
    //return "Link "+id_+", len="+getLengthFeet(); 
    Rectangle2D r = getBoundingBox();
    return "["+scenario_.getName()+"] Link "+id_+" ("+fNode_.getID()+" to "+tNode_.getID()+ ") / len=" + getLengthFeet();
  }

  public void setName(String name) {
    displayName_ = name;
  }

  /*public double getLength() {
    return len_;
  }*/

  public int getLengthFeet() {
    return (int) lenFt_;
  }

  public double getLengthFeetDbl() {
    return lenFt_;
  }

  public double getMeasuredLength() {
    Iterator<Point2D.Double> shpPts = shpPts_.iterator();
    double x1 = x1_, y1 = y1_, x2, y2;

    double len = 0;
    while (shpPts.hasNext()) {
      Point2D.Double shpPt = shpPts.next();
      x2 = shpPt.getX();
      y2 = shpPt.getY();
      len += FPUtil.magnitude(x1, y1, x2, y2);
      x1 = x2;
      y1 = y2;
    }
    x2 = x2_;
    y2 = y2_;
    len += FPUtil.magnitude(x1, y1, x2, y2);
    return len;
  }

  public void setLengthFeet(double lenFt) {
    lenFt_ = lenFt;
  }

  public int shapePointsCount() {
    return shpPts_.size();
  }

  public List<Point2D.Double> getShapePointsList() {
    return shpPts_;
  }

  public List<Point2D.Double> getShapePointsListCopy() {
    List<Point2D.Double> copy = new LinkedList<Point2D.Double>();
    for(Point2D.Double pt : shpPts_) copy.add(new Point2D.Double(pt.x, pt.y));
    return copy;
  }

  public Iterator<Point2D.Double> getShapePoints() {
    return shpPts_.iterator();
  }

  public Point2D.Double getNearestShapePoint(BNode node) {

    if (node == fNode_) {
      Point2D.Double pt = fNode_.toPoint();
      for (int i = 0; i < shpPts_.size(); i++) {
        if (!shpPts_.get(i).equals(pt)) {
          return shpPts_.get(i);
        }
      }
      return tNode_.toPoint();
    } else if (node == tNode_) {
      Point2D.Double pt = tNode_.toPoint();
      for (int i = shpPts_.size() - 1; i >= 0; i--) {
        if (!shpPts_.get(i).equals(pt)) {
          return shpPts_.get(i);
        }
      }
      return fNode_.toPoint();
    }
    System.err.println("Error in CLink:getNearestShapePoint(): node is not an endpoint");
    return null;
  }

  public byte[] getShapePointsBytes() {
    try {
      ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
      ObjectOutputStream objOS = new ObjectOutputStream(byteOS);
      int sp = 0;
      Iterator<Point2D.Double> shpPts = getShapePoints();
      while (shpPts.hasNext()) {
        Point2D.Double pt = shpPts.next();
        objOS.writeDouble(pt.x);
        objOS.writeDouble(pt.y);
        sp++;
      }
      System.out.println("sp count=" + sp);
      objOS.close();
      return byteOS.toByteArray();
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    return null;
  }

  public void setShapePoints(List<Point2D.Double> shpPts) {
    shpPts_ = shpPts;
    updateBBXY();
  //System.out.println("shp pts registered: "+shpPts_.size());
  }

  public int getShapePointIndexFromXY(double x, double y, double tol) {
    int index = -1;
    Iterator<Point2D.Double> shpPts = shpPts_.iterator();

    int i = 0;
    while (shpPts.hasNext()) {
      Point2D.Double shpPt = shpPts.next();
      double spx = shpPt.getX(), spy = shpPt.getY();
      if (FPUtil.magnitude(x, y, spx, spy) < tol) {
        index = i;
      }
      i++;
    }
    return index;
  }

  public void deleteShapePoint(int i) {
    shpPts_.remove(i);
  }

  public void addShapePoint(int index, double x, double y) {
    shpPts_.add(index, new Point2D.Double(x, y));
  }

  public void simplifyShapePoints() {

    if (shpPts_.size() == 0) {
      return;
    }

    List<Point2D.Double> newShpPts = new LinkedList<Point2D.Double>();
    Iterator<Point2D.Double> shpPts = shpPts_.iterator();

    int i = 0;
    while (shpPts.hasNext()) {
      Point2D.Double shpPt = shpPts.next();
      i++;
      if (i % 2 == 0) {
        newShpPts.add(shpPt);
      }
    }

    shpPts_ = newShpPts;

  }

  public Point2D.Double[] getPoint(double i) {
    Iterator<Point2D.Double> shpPts = shpPts_.iterator();
    double pctThrough = 0;
    double x1 = x1_, y1 = y1_, x2, y2;
    boolean debug = false; //(id_ == 1000640);
    if (debug) {
      System.out.println("i=" + i);
    }

    while (shpPts.hasNext()) {
      Point2D.Double shpPt = shpPts.next();
      x2 = shpPt.getX();
      y2 = shpPt.getY();
      double pct = FPUtil.magnitude(x1, y1, x2, y2) / this.getLengthFeetDbl();
      if (debug) {
        System.out.println(" pct=" + pct);
      }
      if (pctThrough + pct > i) {
        double pctAlongSeg = (i - pctThrough) / pct;
        if (debug) {
          System.out.println("  pctAS=" + pctAlongSeg);
        }
        Point2D.Double retArr[] = new Point2D.Double[2];
        retArr[0] = new Point2D.Double(x1 + (pctAlongSeg * (x2 - x1)), y1 + (pctAlongSeg * (y2 - y1)));
        retArr[1] = new Point2D.Double(x2 - x1, y2 - y1);
        return retArr;
      }
      pctThrough += pct;
      x1 = x2;
      y1 = y2;
    }
    x2 = x2_;
    y2 = y2_;
    double pctAlongSeg = i - pctThrough;
    
    
    Point2D.Double retArr[] = new Point2D.Double[2];
    retArr[0] = new Point2D.Double(x1 + (pctAlongSeg * (x2 - x1)), y1 + (pctAlongSeg * (y2 - y1)));
    retArr[1] = new Point2D.Double(x2 - x1, y2 - y1);
    return retArr;
  }

  public abstract boolean isWalkable();

  public abstract boolean isBikeable(BNode start);

  public boolean isMajor() {
    return false;
  }

  public int getElevationAt(double dist, boolean reverse) {
    return 0;
  }

  public float getUpSlopeFW() {
    return upSlopeFW_;
  }

  public void setUpSlopeFW(float val) {
    upSlopeFW_ = (val > 5) ? 5 : val;
  }

  public float getUpSlopeBW() {
    return upSlopeBW_;
  }

  public void setUpSlopeBW(float val) {
    upSlopeBW_ = (val > 5) ? 5 : val;
  }

  public float getUpSlope(BNode from) {
    if (from == fNode_) {
      return upSlopeFW_;
    }
    if (from == tNode_) {
      return upSlopeBW_;
    }
    return 0;
  }

  public void setElevStr(String elevStr) {
    if (elevStr.startsWith("0")) {
      return;
    }
    //System.out.println(elevStr);
    String[] arr = elevStr.split(",");
    elevArr_ = new int[arr.length];
    for (int i = 0; i < arr.length; i++) {
      String s = arr[i].trim();
      //System.out.println("("+s+")");
      try {
        if (s.length() > 0) {
          elevArr_[i] = new Integer(s).intValue();
        }

      } catch (Exception e) {
        System.out.println("invalid elevation: " + s);
      }
    }
  }

  public void setElevList(List<Integer> list) {
    elevArr_ = new int[list.size()];

    for (int i = 0; i < list.size(); i++) {
      elevArr_[i] = list.get(i).intValue();
    }
  }

  public int[] getElevArray() {
    return elevArr_;
  }

  public byte[] getElevBytes() {
    try {
      ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
      ObjectOutputStream objOS = new ObjectOutputStream(byteOS);
      int c = 0;
      for (int i = 0; i < elevArr_.length; i++) {
        objOS.writeInt(elevArr_[i]);
        c++;
      }
      //System.out.println("elev count="+c);
      objOS.close();
      return byteOS.toByteArray();
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    return null;
  }

  public double getElevation(double dist) {
    return 0;
  }

  public int getMinElev() {
    if (elevArr_ == null) return Integer.MAX_VALUE;
    int min = Integer.MAX_VALUE;
    for (int i = 0; i < elevArr_.length; i++) {
      if(elevArr_[i] != 0) min = Math.min(min, elevArr_[i]);
    }
    return min;
  }

  public int getMaxElev() {
    if (elevArr_ == null) {
      return 0;
    }
    int max = Integer.MIN_VALUE;
    for (int i = 0; i < elevArr_.length; i++) {
      max = Math.max(max, elevArr_[i]);
    }
    return max;
  }

  //////////////////// DRAW METHODS ////////////////////

  /*public void draw(MapCanvas canvas) {
  }*/

  public abstract void drawBG(MapCanvas canvas, Scenario scen);

  public abstract void drawFG(MapCanvas canvas, Scenario scen);

  public abstract int getDrawWidth(MapCanvas canvas, Scenario scen);

  public void drawShapePoints(MapCanvas canvas) {
    Iterator<Point2D.Double> shpPts = shpPts_.iterator();

    while (shpPts.hasNext()) {
      Point2D.Double shpPt = shpPts.next();
      double x = shpPt.getX(), y = shpPt.getY();
      canvas.setColor(Color.BLACK);
      canvas.drawPoint(x, y, 4);
      canvas.setColor(Color.WHITE);
      canvas.drawPoint(x, y, 2);
    }

  }

  public void drawCurve(MapCanvas canvas) {
    drawCurve(canvas, 0, 8);
  }

  public void drawCurve(MapCanvas canvas, int drawArrow, double maxArrowLen) {
    int numPts = 2 + shpPts_.size();
    double xArr[] = new double[numPts];
    double yArr[] = new double[numPts];

    xArr[0] = x1_;
    yArr[0] = y1_;
    Iterator<Point2D.Double> shpPts = shpPts_.iterator();
    int i = 1;
    while (shpPts.hasNext()) {
      Point2D.Double shpPt = shpPts.next();
      xArr[i] = shpPt.getX();
      yArr[i] = shpPt.getY();
      i++;
    }
    xArr[i] = x2_;
    yArr[i] = y2_;

    canvas.drawPolyline(xArr, yArr, numPts);
    if (drawArrow > 0) {
      drawArrow(canvas, maxArrowLen, drawArrow == 2);
    }
  }

  public void drawArrow(MapCanvas canvas, double maxLen, boolean reverse) {
    canvas.drawArrow(this, maxLen, reverse);
  }

  public void drawSel(MapCanvas canvas) {
    canvas.setColor(Color.yellow);
    canvas.setStroke(new BasicStroke(5));
    drawCurve(canvas);
  }

  //////////////////// XML METHODS ////////////////////

  public String getXML() {
    String ret = "";
    ret = ret + "<link type=\"" + getClassType() + "\" id=\"" + id_ + "\" fnode=\"" + fNode_.getID() + "\" tnode=\"" + tNode_.getID() + "\" name=\"" + getDisplayName() + "\" len=\"" + lenFt_ + "\"" + getMoreXML() + ">";
    Iterator<Point2D.Double> shpPts = shpPts_.iterator();
    while (shpPts.hasNext()) {
      Point2D.Double sp = shpPts.next();
      ret = ret + "<sp x=\"" + sp.getX() + "\" y=\"" + sp.getY() + "\" />";
    }
    ret = ret + "</link>";
    return ret;
  }

  public String getMoreXML() {
    return "";
  }


  //////////////////// PATH REGISTRATION METHODS ////////////////////

  public void registerPath(Path path) {
    paths_.add(path); //insertLast(path);
  }

  public void unregisterPath(Path path) {
    paths_.remove(path);
  }

  public int registeredPathCount() {
    return paths_.size();
  }

  public Collection<Path> getRegisteredPaths() {
    return paths_;
  }

  //////////////////// TRANSIT METHODS ////////////////////


  public abstract boolean supportsTransitType(TransitPath.Type type, Scenario scen, BaseNetwork baseNet);

  public boolean supportsTransitType(TransitPath.Type type, BaseNetwork baseNet) {
    return supportsTransitType(type, scenario_, baseNet);
  }

  public void registerSubRouteFT(SubRoute subft, int dli) {
    Iterator<SubRouteInfo> srisFT = srisFT_.iterator();
    while (srisFT.hasNext()) {
      SubRoute cursub = srisFT.next().getSubRoute();
      if (cursub == subft) {
        return;
      }
    }
    srisFT_.add(new SubRouteInfo(subft, dli));
  }

  public int subRouteCount() {
    return srisFT_.size() + srisTF_.size();
  }

  public Iterator<SubRouteInfo> getSubRouteInfoFT() {
    //System.out.println("ret sriFT: "+subsFT_.size());
    return srisFT_.iterator();
  }

  public Iterator<SubRoute> getSubRoutesFT() {
    // convert SRI's to SR's
    List<SubRoute> toRet = new LinkedList<SubRoute>();
    Iterator<SubRouteInfo> srisFT = srisFT_.iterator();
    while (srisFT.hasNext()) {
      toRet.add(srisFT.next().getSubRoute());
    }
    return toRet.iterator();
  }

  public void registerSubRouteTF(SubRoute subtf, int dli) {
    Iterator<SubRouteInfo> srisTF = srisTF_.iterator();
    while (srisTF.hasNext()) {
      SubRoute cursub = srisTF.next().getSubRoute();
      if (cursub == subtf) {
        return;
      }
    }
    srisTF_.add(new SubRouteInfo(subtf, dli));
  }

  public Iterator<SubRouteInfo> getSubRouteInfoTF() {
    //System.out.println("ret sriTF: "+subsTF_.size());
    return srisTF_.iterator();
  }

  public Iterator<SubRoute> getSubRoutesTF() {
    // convert SRI's to SR's
    List<SubRoute> toRet = new LinkedList<SubRoute>();
    Iterator<SubRouteInfo> srisTF = srisTF_.iterator();
    while (srisTF.hasNext()) {
      toRet.add(srisTF.next().getSubRoute());
    }
    return toRet.iterator();
  }

  public Iterator<SubRoute> getAllSubRoutes() {
    List<SubRoute> toRet = new LinkedList<SubRoute>();

    // convert SRI's to SR's
    Iterator<SubRouteInfo> srisFT = srisFT_.iterator();
    while (srisFT.hasNext()) {
      toRet.add(srisFT.next().getSubRoute());
    }

    // convert TF SRI's to SR's
    Iterator<SubRouteInfo> srisTF = srisTF_.iterator();
    while (srisTF.hasNext()) {
      toRet.add(srisTF.next().getSubRoute());
    }

    return toRet.iterator();
  }

  public void unregisterAllSubRoutes() {
    srisFT_.clear();
    srisTF_.clear();
  //while(!subsFT_.isEmpty()) subsFT_.removeLast();
  //while(!subsTF_.isEmpty()) subsTF_.removeLast();
  }

  public Station getStation() {
    return station_;
  }

  public void setStation(Station station) {
    station_ = station;
  }

  public void removeStation() {
    station_ = null;
  //stations_--; 
  //if(stations_<0) System.err.println("Warning: stations_ less than 0 for node #"+id_);
  }

  public boolean isStation() {
    return (station_ != null);
  }

  //////////////////// UTILITY METHODS ////////////////////
  public boolean canDelete(GUIOutputAcceptor oa) {
    /*if(isStation()) {
    oa.msg("Cannot Delete Station Link");
    return false;
    }*/
    if (registeredPathCount() > 0) {
      oa.msg("Cannot delete; link belongs to the following active path(s):");
      int i = 1;
      for(Path path : getRegisteredPaths()) {
        if (path.isTransitPath()) {
          oa.msg(i + ". Transit Path for " + ((TransitPath) path).getSubRoute().getFullID());
        } else {
          oa.msg(i + ". General path of length " + path.linkCount());
        }
        i++;
      }
      return false;
    }
    return true;
  }

  public double distToPoint(double wx, double wy) {
    /*double minDist = Double.MAX_VALUE, dist;
    double x1 = getX1(), y1 = getY1(), x2 = getX2(), y2 = getY2();
    double lineMag = CUtil.magnitude(x1,y1,x2,y2);
    double u = ((wx-x1)*(x2-x1)+(wy-y1)*(y2-y1))/(lineMag*lineMag);
    if(u > Double.MIN_VALUE && u <= 1) {
    double ix = x1 + u*(x2-x1);
    double iy = y1 + u*(y2-y1);
    dist = CUtil.magnitude(ix,iy,wx,wy);
    if(dist<minDist) {
    minDist = dist;
    }
    }
    return minDist;*/
    //System.out.println("    * in dTP ("+wx+", "+wy+")");

    Iterator<Point2D.Double> shpPts = shpPts_.iterator();
    double x1 = x1_, y1 = y1_, x2, y2;
    double minDist = Double.MAX_VALUE, dist;

    while (shpPts.hasNext()) {
      Point2D.Double shpPt = shpPts.next();
      x2 = shpPt.getX();
      y2 = shpPt.getY();
      dist = Line2D.ptSegDist(x1, y1, x2, y2, wx, wy); //FPUtil.distToSegment(wx, wy, x1, y1, x2, y2);
      if (dist < minDist) {
        minDist = dist;
      }
      x1 = x2;
      y1 = y2;
    }

    x2 = x2_;
    y2 = y2_;
    dist = Line2D.ptSegDist(x1, y1, x2, y2, wx, wy); //FPUtil.distToSegment(wx, wy, x1, y1, x2, y2);
    if (dist < minDist) {
      minDist = dist;
    }

    //System.out.println("    * dTP returning "+minDist);
    return minDist;
  }

  public Coordinate[] toCoordArray() {
    Coordinate[] coordArr = new Coordinate[2 + shpPts_.size()];
    coordArr[0] = new Coordinate(x1_, y1_);
    coordArr[coordArr.length - 1] = new Coordinate(x2_, y2_);
    for (int i = 0; i < shpPts_.size(); i++) {
      Point2D.Double pt = shpPts_.get(0);
      coordArr[i + 1] = new Coordinate(pt.x, pt.y);
    }
    return coordArr;
  }

  public Rectangle2D getBoundingBox() {

    Coordinate[] coords = toCoordArray();

    if (coords.length == 0) {
      return null;
    }

    double x1 = Double.POSITIVE_INFINITY, y1 = Double.POSITIVE_INFINITY;
    double x2 = Double.NEGATIVE_INFINITY, y2 = Double.NEGATIVE_INFINITY;

    for (int i = 0; i < coords.length; i++) {
      x1 = Math.min(x1, coords[i].x);
      y1 = Math.min(y1, coords[i].y);
      x2 = Math.max(x2, coords[i].x);
      y2 = Math.max(y2, coords[i].y);
    }
    //System.out.println("link bb "+x1+" "+y1+" "+(x2-x1)+" "+(y2-y1));

    return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
  }
}













































